package main

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	pb "ihos/ihos"
	"ihos/ihos_service/tencentyun"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	tm "github.com/golang/protobuf/ptypes/timestamp"
)

const VIDEO_REQUEST_TIMEOUT = 30

func (c *server) _generateAccessToken(lx pb.YHLX, name string, rand int) string {
	md5Context := md5.New()
	md5Context.Write([]byte(lx.String() + name + strconv.Itoa(rand)))
	return strings.ToUpper(hex.EncodeToString(md5Context.Sum(nil)))
}

func (c *server) TokenVerify(ctx context.Context, in *pb.DengLuShuJuL1) (*pb.ReturnMessage, error) {
	if in.GetDLYXQ().GetSeconds() < time.Now().Unix() {
		return &pb.ReturnMessage{Code: pb.ReturnMessage_ERROR, Message: "ERROR"}, nil
	}
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: "OK"}, nil
}

func (c *server) HuanZheRegister(ctx context.Context, in *pb.HuanZheInfoL3) (*pb.ReturnMessage, error) {
	log.Println("call HuanZheRegister", in)
	//Check Must-Have fields
	if in.GRXX.GetWX() == nil {
		return nil, errors.New("MISS Weixin Info")
	}
	if len(in.GetGRXX().GetWX().GetOpenId()) <= 0 {
		return nil, errors.New("MISS Weixin openId")
	}
	if len(in.GetGRXX().GetWX().GetUnionId()) <= 0 {
		return nil, errors.New("MISS Weixin unoinId")
	}
	if len(in.GetGRXX().GetWX().GetAppId()) <= 0 {
		return nil, errors.New("MISS Weixin appId")
	}
	// if len(in.GetGRXX().GetSJHM()) <= 0 {
	// 	return nil, errors.New("MISS ShouJiHaoMa")
	// }
	if len(in.GetDLXX().GetJS().GetJSMC()) <= 0 {
		return nil, errors.New("MISS JiaoSe MingCheng")
	}
	if len(in.GetDLXX().GetDL().GetDLMC()) <= 0 {
		return nil, errors.New("MISS DengLu MingCheng")
	}
	if len(in.GetDLXX().GetDL().GetDLJY()) <= 0 {
		return nil, errors.New("MISS DengLu MiMa")
	}
	//Save or Update basicinfo
	b := new(basicinfo)
	if err := b.fromHZPB(in).SaveToDB(b.id); err != nil {
		log.Println(err)
		return nil, err
	}
	// log.Println("B:", b)
	in.GRXX.ID = b.id
	//Save or Update denglu
	d := new(denglu)
	if err := d.fromPB(in.GetDLXX()).SaveToDB(d.id); err != nil {
		log.Println(err)
		return nil, err
	}
	in.DLXX.JS.ID = d.id
	//get from huanzhe by grxx and dlxx
	h := new(huanzhe)
	if err := h.fromPB(in).SaveToDB(h.id); err != nil {
		log.Println(err)
		return nil, err
	}
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: fmt.Sprint(h.id)}, nil
}

func (s *server) HuanZheHelpRegister(ctx context.Context, in *pb.HuanZheInfoL3) (*pb.ReturnMessage, error) {
	log.Println("call HuanZheHelpRegister", in)
	if len(in.GetGRXX().GetSFZ()) <= 0 {
		return nil, errors.New("MISS SFZ")
	}
	if len(in.GetGRXX().GetSJHM()) <= 0 {
		return nil, errors.New("MISS ShouJiHaoMa")
	}
	//check if exist
	hz := &pb.HuanZheInfoL3{
		GRXX: &pb.BasicInfoL1{SFZ: in.GRXX.GetSFZ()},
	}
	hzs, err := s.GetHuanZheInfo(ctx, hz)
	if err == nil && len(hzs.GetHZS()) > 0 {
		return nil, errors.New("User Existed")
	}
	//Save or Update basicinfo
	b := new(basicinfo)
	if err := b.fromHZPB(in).SaveToDB(b.id); err != nil {
		log.Println(err)
		return nil, err
	}
	// log.Println("B:", b)
	in.GRXX.ID = b.id
	//get from huanzhe by grxx and dlxx
	h := new(huanzhe)
	if err := h.fromPB(in).SaveToDB(h.id); err != nil {
		log.Println(err)
		return nil, err
	}
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: fmt.Sprint(h.id)}, nil
}

func (c *server) KeFuRegister(ctx context.Context, in *pb.KeFuInfoL3) (*pb.ReturnMessage, error) {
	log.Println("call KeFuRegister", in)
	//Check Must-Have fields
	if len(in.GetGRXX().GetSJHM()) <= 0 {
		return nil, errors.New("MISS ShouJiHaoMa")
	}
	if len(in.GetDLXX().GetJS().GetJSMC()) <= 0 {
		return nil, errors.New("MISS JiaoSe MingCheng")
	}
	if len(in.GetDLXX().GetDL().GetDLMC()) <= 0 {
		return nil, errors.New("MISS DengLu MingCheng")
	}
	if len(in.GetDLXX().GetDL().GetDLJY()) <= 0 {
		return nil, errors.New("MISS DengLu MiMa")
	}
	in.ID = 0
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: fmt.Sprint(in.GetID())}, nil
}

func (c *server) YiShengRegister(ctx context.Context, in *pb.YiShengInfoL3) (*pb.ReturnMessage, error) {
	log.Println("call KeFuRegister", in)
	//Check Must-Have fields
	if len(in.GetGRXX().GetSJHM()) <= 0 {
		return nil, errors.New("MISS ShouJiHaoMa")
	}
	if len(in.GetDLXX().GetJS().GetJSMC()) <= 0 {
		return nil, errors.New("MISS JiaoSe MingCheng")
	}
	if len(in.GetDLXX().GetDL().GetDLMC()) <= 0 {
		return nil, errors.New("MISS DengLu MingCheng")
	}
	if len(in.GetDLXX().GetDL().GetDLJY()) <= 0 {
		return nil, errors.New("MISS DengLu MiMa")
	}
	in.ID = 0
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: fmt.Sprint(in.GetID())}, nil
}

func (c *server) Login(ctx context.Context, in *pb.DengLuL2) (*pb.DengLuShuJuL1, error) {
	log.Println("call Login", in)
	w := &denglu{}
	switch w.Check(in.GetDL().GetDLMC(), in.GetDL().GetDLJY(), in.GetJS().GetJSDW(), int32(in.GetJS().GetJSLX())) {
	case 1:
		return &pb.DengLuShuJuL1{DLZT: pb.DengLuShuJuL1_FAIL}, nil
	case 2:
		return &pb.DengLuShuJuL1{DLZT: pb.DengLuShuJuL1_NOT_EXIST}, nil
	}
	//Get Account
	aid := int32(0)
	if in.GetJS().GetJSLX() == pb.YHLX_HUANZHE {
		h := &huanzhe{}
		if err := h.GetByDlxx(w.id); err != nil {
			log.Println(err)
			return nil, err
		}
		aid = h.id
	} else if in.GetJS().GetJSLX() == pb.YHLX_YISHENG {
		h := &yisheng{}
		if err := h.GetByDlxx(w.id); err != nil {
			log.Println(err)
			return nil, err
		}
		aid = h.id
	} else if in.GetJS().GetJSLX() == pb.YHLX_KEFU {
		// log.Println("Get KeFu", w.id)
		h := &kefu{}
		if err := h.GetByDlxx(w.id); err != nil {
			log.Println(err)
			return nil, err
		}
		aid = h.id
	} else {
		return &pb.DengLuShuJuL1{DLZT: pb.DengLuShuJuL1_FAIL}, nil
	}
	rand.Seed(time.Now().UnixNano())
	out := &pb.DengLuShuJuL1{
		DLZT: pb.DengLuShuJuL1_OK,
		//FWPZ:  c._generateAccessToken(in.GetJS().GetJSLX(), in.GetDL().GetDLMC(), rand.Intn(9999999)),
		FWPZ:  c._generateAccessToken(in.GetJS().GetJSLX(), in.GetDL().GetDLMC(), 9999999),
		DLSJ:  &tm.Timestamp{Seconds: time.Now().Unix()},
		DLYXQ: &tm.Timestamp{Seconds: time.Now().AddDate(0, 1, 0).Unix()},
		JSLX:  in.GetJS().GetJSLX(),
		DLMC:  in.GetDL().GetDLMC(),
		AID:   aid,
	}
	d := new(denglu_shuju).fromPB(out)
	if err := d.SaveToDBByAccount(aid, int32(in.GetJS().GetJSLX())); err != nil {
		return nil, err
	} else {
		log.Println("Login Out:", out)
		return out, nil
	}
}

func (c *server) GetHuanZheInfo(ctx context.Context, in *pb.HuanZheInfoL3) (*pb.HuanZheListL4, error) {
	log.Println("call GetHuanZheInfo V4", in)
	if in.GetID() > 0 {
		h := &huanzhe{}
		if err := h.GetFromDB(in.GetID()); err != nil {
			log.Println(err)
			return nil, err
		}
		//DataBase to protobuf
		// log.Println(h)
		out := new(pb.HuanZheListL4)
		out.HZS = append(out.HZS, h.toPB())
		return out, nil
	} else {
		//todo: get by XM, SFZ, WX
		b := &basicinfo{}
		if bis, err := b.fromPB(in.GetGRXX()).GetBasicInfoList(); err != nil {
			log.Println(err)
			return nil, err
		} else {
			log.Println("BasicInfo:", bis)
			if len(bis) > 0 {
				out := new(pb.HuanZheListL4)
				for i := 0; i < len(bis); i++ {
					h := &huanzhe{}
					if err := h.GetByGrxx(bis[i].id); err != nil {
						log.Println(err)
						// return nil, err
					} else {
						out.HZS = append(out.HZS, h.toPB())
					}
				}
				return out, nil
			} else {
				return nil, errors.New("NOT EXIST")
			}
		}
	}
}

func (c *server) GetYiShengInfo(ctx context.Context, in *pb.YiShengInfoL3) (*pb.YiShengListL4, error) {
	log.Println("call GetYiShengInfo", in)
	if in.GetID() > 0 {
		h := &yisheng{}
		if err := h.GetFromDB(in.GetID()); err != nil {
			return nil, err
		}
		//DataBase to protobuf
		out := new(pb.YiShengListL4)
		out.YSS = append(out.YSS, h.toPB())
		return out, nil
	}
	//todo: get by XM, SFZ, WX
	return nil, errors.New("NOT EXIST")
}

func (c *server) GetKeFuInfo(ctx context.Context, in *pb.KeFuInfoL3) (*pb.KeFuInfoL3, error) {
	log.Println("call GetKeFuInfo", in)
	if in.GetID() > 0 {
		h := &kefu{}
		if err := h.GetFromDB(in.GetID()); err != nil {
			log.Println(err)
			return nil, err
		}
		//DataBase to protobuf
		return h.toPB(), nil
	}
	//todo: get by XM, SFZ, WX
	return nil, errors.New("NOT EXIST")
}

func (c *server) SaveHuanZheInfo(ctx context.Context, in *pb.HuanZheInfoL3) (*pb.ReturnMessage, error) {
	log.Println("SaveHuanZheInfo", in)
	hz := new(huanzhe)
	//no auto create new huanzhe
	if err := hz.GetFromDB(in.GetID()); err != nil {
		log.Println(err)
		return nil, err
	}
	hzp := hz.toPB()
	if in.GetGRXX() != nil {
		hzp.GRXX.XM = in.GetGRXX().GetXM()
		hzp.GRXX.XB = in.GetGRXX().GetXB()
		hzp.GRXX.CSRQ = in.GetGRXX().GetCSRQ()
		hzp.GRXX.SFZ = in.GetGRXX().GetSFZ()
		hzp.GRXX.SJHM = in.GetGRXX().GetSJHM()
		hzp.GRXX.WX.Account = in.GetGRXX().GetWX().GetAccount()
		// hzp.JZK[0]
		hzp.JTDZ.DZ = in.GetJTDZ().GetDZ()
		hzp.JTDZ.YB = in.GetJTDZ().GetYB()
		hzp.JTDZ.JD = in.GetJTDZ().GetJD()
		hzp.JTDZ.WD = in.GetJTDZ().GetWD()
		hzp.JTDZ.SHENG = in.GetJTDZ().GetSHENG()
		hzp.JTDZ.SHI = in.GetJTDZ().GetSHI()
		hzp.JTDZ.QU = in.GetJTDZ().GetQU()
		b := new(basicinfo).fromHZPB(hzp)
		if err := b.SaveToDB(b.id); err != nil {
			log.Println(err)
			return nil, err
		}
	}
	if in.GetJZR() != nil {
		new(jiuzhenren).RemoveAllByHz(in.GetID())
		for _, v := range in.GetJZR() {
			j := new(jiuzhenren).fromPB(v)
			j.hzid = in.GetID()
			if err := j.SaveToDB(0); err != nil {
				log.Println(err)
				return nil, err
			}
		}
	}
	if in.GetYY() != nil {
		hzp.YY = in.GetYY()
	}
	hz.fromPB(hzp)
	if err := hz.SaveToDB(hz.id); err != nil {
		return &pb.ReturnMessage{
			Code:    pb.ReturnMessage_ERROR,
			Message: "FAIL",
		}, nil
	} else {
		return &pb.ReturnMessage{
			Code:    pb.ReturnMessage_OK,
			Message: "OK",
		}, nil
	}
}

type WXSessionJSON struct {
	SessionKey string `json:"session_key"`
	OpenID     string `json:"openid"`
	UnionID    string `json:"unionid"`
}

func (c *server) GetWXSession(ctx context.Context, in *pb.WXCode) (*pb.WXSession, error) {
	log.Println("call GetWXSession", in)
	appid := ""
	secret := ""
	grant_type := "authorization_code"
	switch in.GetNAME() {
	case "BHKF":
		appid = "wx3bfb0cb7d2665161"
		secret = "7e1898495c910129ae7772c6abb77740"
	case "DYJZ":
		appid = "wx76a4fd8f957b62ef"
		secret = "36a12c86106a3f88ddd3d61f82f4c4a2"
	case "YJHJ":
		appid = "wx71a7dad3b8abd182"
		secret = "b028961e9ba90ecffb20cc54904a2fc5"
	case "YJZX":
		appid = "wx647d483a7c732e84"
		secret = "cc9c9974da3e679ed552b99361756ac8"
	case "LXHL":
		appid = "wxa1d2ff6815b3650c"
		secret = "c68750407e4cc95a917a054790fd390c"
	}
	// log.Println(appid, secret, grant_type)
	url := "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secret + "&js_code=" + in.GetCODE() + "&grant_type=" + grant_type
	// log.Println(url)
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// fmt.Println(string(body))
	//e.g. {"session_key":"UEtEFdBgRrv3GBBBYRcnJA==","openid":"ownn25W7jIcLL4mnAS4zWRTOGzz8","unionid":"oZJY10vD6kXy8xvI6NUEC21FoZ4s"}
	var wxj WXSessionJSON
	if err := json.Unmarshal(body, &wxj); err != nil {
		return nil, err
	} else {
		s := wx_session{
			session: wxj.SessionKey,
			openid:  wxj.OpenID,
			unionid: wxj.UnionID,
			appid:   appid,
			appname: in.GetNAME(),
		}
		if err := s.SaveToDB(s.id); err != nil {
			return nil, err
		} else {
			return s.toPB(), nil
		}
	}
}

func DecryptWXOpenData(sessionKey, encryptData, iv string) (string, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(encryptData)
	if err != nil {
		return "", err
	}
	sessionKeyBytes, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return "", err
	}
	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return "", err
	}
	dataBytes, err := AesDecrypt(decodeBytes, sessionKeyBytes, ivBytes)
	if err != nil {
		return "", err
	}
	// fmt.Println(string(dataBytes))
	return string(dataBytes), nil
}

func AesDecrypt(crypted, key, iv []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	// log.Println("ENCDATA:", crypted)
	// log.Println("KEY:", key)
	// log.Println("IV:", iv)
	//blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, iv)
	origData := make([]byte, len(crypted))
	blockMode.CryptBlocks(origData, crypted)
	// log.Println("ORGDATA:", []byte(origData))
	//获取的数据尾端有占位符,去除它,占位符即为padding长度
	// log.Println("ORGDATA LEN:", len(origData))
	plen := origData[len(origData)-1]
	// log.Println("PAD LEN:", plen)
	if plen <= 16 {
		rlen := len(origData) - int(plen)
		origData = origData[:rlen]
	}
	// log.Println("ORGDATA UNPADDING:", len(origData))
	//{"phoneNumber":"15082726017","purePhoneNumber":"15082726017","countryCode":"86","watermark":{"timestamp":1539657521,"appid":"wx4c6c3ed14736228c"}}//<nil>
	return origData, nil
}

func (c *server) DecodeWXUserInfo(ctx context.Context, in *pb.WXEncData) (*pb.ReturnMessage, error) {
	log.Println("call DecodeWXUserInfo", in)
	session := ""
	if len(in.GetSESSION()) > 0 {
		session = in.GetSESSION()
	} else {
		//get SESSION by key
		wx := new(wx_session)
		if err := wx.GetFromDB(in.GetSESSIONID()); err != nil {
			return nil, err
		}
		session = wx.session
	}
	if len(in.GetENCRYPTEDDATA()) > 0 && len(in.GetIV()) > 0 {
		m, err := DecryptWXOpenData(session, in.GetENCRYPTEDDATA(), in.GetIV())
		if err != nil {
			return nil, err
		} else {
			return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: m}, nil
		}
	} else {
		return nil, errors.New("INVALID DATA")
	}
}

// const ZY_URL = "http://172.30.10.36:8886/Ecis-service/WSEcisInterface"
// const ZY_URL = "http://100.100.100.143:8886/Zhikang-service/WSZhikangInterface?wsdl"
// const ZY_URL = "http://222.187.112.58:8886/Zhikang-service/WSZhikangInterface?wsdl"
// 内网服务地址 100.100.100.143 8886  --- 221.6.215.250.8886
// 内网服务器地址 172.30.10.36  8886 ---- 221.6.215.250 8887
const ZY_TEST_URL = "http://221.6.215.250:8886/Zhikang-service/WSZhikangInterface"
const ZY_FORMAL_URL = "http://221.6.215.250:8887/Zhikang-service/WSZhikangInterface"

func (c *server) CallZYAPI(ctx context.Context, in *pb.ZYAPIParam) (*pb.ReturnMessage, error) {
	log.Println("call CallZYAPI", in)

	serverAdd := ZY_TEST_URL
	if in.ISFORMAL == 1 {
		serverAdd = ZY_FORMAL_URL
	}
	reqBody := `<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:zoes="http://www.zoesoft.com.cn/">
		<soap:Header/>
		<soap:Body>
		   <zoes:CallInterface>
			  <zoes:msgHeader><![CDATA[<?xml version="1.0" encoding="utf-8"?> <root><serverName>` + in.GetMSGHEADER() + `</serverName><format>xml</format><callOperator>一键呼叫系统</callOperator><certificate>yvQfG4aRsJo+0eBo+0AK8GvANwidgNDd</certificate> </root>]]></zoes:msgHeader><zoes:msgBody><![CDATA[` + in.GetMSGBODY() + `]]></zoes:msgBody>
		   </zoes:CallInterface>
		</soap:Body>
	 </soap:Envelope>`
	// log.Println(reqBody)
	logFileName := in.GetMSGHEADER()
	t := time.Now().Format("20060102150405")
	os.WriteFile(logFileName+"_req_"+t+".xml", []byte(reqBody), 0644)
	result := ""
	switch in.GetTYPE() {
	case 0:
		// log.Println("Post Message")
		res, err := http.Post(serverAdd, "Content-Type: application/soap+xml;charset=utf-8;action=\"urn:CallInterface\"", strings.NewReader(reqBody))
		if nil != err {
			fmt.Println("http post err:", err)
			return nil, errors.New("CALL ZYAPI Failed")
		}
		defer res.Body.Close()
		// log.Println("Post Message return:", res.StatusCode)
		if 200 != res.StatusCode {
			return nil, errors.New("Call ZYAPI returns error code " + string(res.StatusCode))
		}
		data, err := io.ReadAll(res.Body)
		if nil != err {
			fmt.Println("io ReadAll err:", err)
			return nil, errors.New("READ ZYAPI RESPONSE FAILED")
		}
		result = string(data)
	case 2:
		res, err := http.Post(serverAdd, "Content-Type: application/soap+xml;charset=utf-8;action=\"urn:CallInterface\"", strings.NewReader(reqBody))
		if nil != err {
			fmt.Println("http post err:", err)
			return nil, errors.New("CALL ZYAPI Failed")
		}
		defer res.Body.Close()
		if 200 != res.StatusCode {
			return nil, errors.New("Call ZYAPI returns error code " + string(res.StatusCode))
		}
		if data, err := io.ReadAll(res.Body); err != nil {
			fmt.Println("io ReadAll err:", err)
			return nil, errors.New("READ ZYAPI RESPONSE FAILED")
		} else {
			os.WriteFile(in.GetFILENAME(), data, 0644)
			os.WriteFile(logFileName+"_res_"+t+".xml", data, 0644)
			result = string(data)
		}
	case 1:
		if data, err := os.ReadFile(in.GetFILENAME()); err != nil {
			log.Println(err)
			return nil, err
		} else {
			result = string(data)
		}
	}
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: result}, nil
}

func (c *server) MakeVideoCall(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ShiPinYongHuL5, error) {
	log.Println("call MakeVideoCall", in)
	if in.GetSPXX() == nil {
		return nil, errors.New("INVALID PARAM")
	}
	//init video create time to now
	if in.GetSPXX().GetCJSJ() == nil || in.GetSPXX().GetCJSJ().GetSeconds() == 0 {
		in.SPXX.CJSJ = &tm.Timestamp{Seconds: time.Now().Unix()}
	}
	in.SPXX.YHID = in.GetYHID()
	in.SPXX.YHLX = in.GetLX()
	s := new(shipin).fromPB(in.GetSPXX())
	if err := s.SaveToDB(s.id); err != nil {
		return nil, err
	}
	in.SPXX.ID = s.id
	in.JRSJ = &tm.Timestamp{Seconds: time.Now().Unix()}
	//Save ShiPinTongHua
	t := new(shipin_yonghu).fromPB(in)
	if err := t.SaveToDB(t.id); err != nil {
		return nil, err
	}
	in.ID = t.id
	//Only valid when in houzhen/menzhen video call
	menZhenPaiDui.Online(in)
	return in, nil
}

func (c *server) RemoveVideoCallRequest(ctx context.Context, in *pb.ShiPinRequestL6) (*pb.ShiPinRequestL6, error) {
	log.Println("call RemoveVideoCallRequest")
	if in.GetZL() == pb.ShiPinRequestL6_UPDATE {
		return in, nil
	}
	s := new(shipin_request).fromPB(in)
	if err := s.Remove(in.GetID()); err != nil {
		return nil, err
	}
	return in, nil
}

func (c *server) AcceptVideoCall(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ShiPinYongHuL5, error) {
	log.Println("call AcceptVideoCall", in)
	in.JRSJ = &tm.Timestamp{Seconds: time.Now().Unix()}

	if in.GetSPXX().GetID() == 0 {
		//If SPXX.ID==0, create it, if kefu switch user to enter HouZhen room
		//May the shipin is not started yet
		return c.MakeVideoCall(ctx, in)
	}
	// in.JRSJ = &tm.Timestamp{Seconds: time.Now().Unix()}
	//Save ShiPinTongHua
	t := new(shipin_yonghu).fromPB(in)
	if err := t.SaveToDB(t.id); err != nil {
		log.Println(err)
		return nil, err
	}
	in.ID = t.id
	//Only valid when in houzhen/menzhen video call
	menZhenPaiDui.Online(in)
	return in, nil
}

func (c *server) HangUpVideoCall(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ShiPinYongHuL5, error) {
	log.Println("call HangUpVideoCall#", in)
	if in.GetID() <= 0 {
		return nil, errors.New("INVALID ShiPinTongHua ID")
	}
	//check if ShiPin is created by this YongHu,
	//if true, then close ShiPin and update GBSJ
	if in.GetSPXX().GetYHID() == in.GetYHID() &&
		in.GetSPXX().GetYHLX() == in.GetLX() {
		s := &shipin{id: in.GetSPXX().GetID()}
		if err := s.GetFromDB(s.id); err != nil {
			log.Println(err)
		} else {
			s.gbsj = time.Now().Unix()
			s.SaveToDB(s.id)
		}
	}
	in.LKSJ = &tm.Timestamp{Seconds: time.Now().Unix()}
	//Save ShiPinTongHua
	t := new(shipin_yonghu).fromPB(in)
	if err := t.SaveToDB(in.GetID()); err != nil {
		return nil, err
	}
	// clear shipin_request
	req := new(shipin_request)
	err := req.GetByFrom(in.ID)
	if err == nil {
		req.sxsj = time.Now().Unix()
		req.SaveToDB(req.id)
	}
	return in, nil
}

func (c *server) ReportVideoCallStatus(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ReturnMessage, error) {
	log.Println("ReportVideoCallStatus", in)
	if in.GetYHID() > 0 {
		stat := UserContent{
			pid:    in.GetYHID(),
			tp:     (int32)(in.GetLX()),
			stat:   USERSTAT_ONLINE,
			device: in.GetRunDevice(),
		}
		stat.user = new(basicinfo)
		stat.user.fromPB(in.GetYHXX())
		if in.GetSPXX() != nil {
			stat.room = in.GetSPXX().GetFJH()
			stat.stat = USERSTAT_IN_VIDEO
		}
		ghs := in.GetXGGHS()
		if len(ghs) > 0 {
			stat.guahao = new(guahao)
			stat.guahao.fromPB(ghs[0])
		}
		UserStatUpdate(stat)
	}
	if in.GetYHID() > 0 && in.GetSPXX() != nil && (len(in.GetSPXX().GetFJH()) > 0) {
		menZhenPaiDui.Online(in)
		out := &pb.ReturnMessage{
			Code:    pb.ReturnMessage_OK,
			Message: in.GetLX().String() + fmt.Sprint(in.GetYHID()),
		}
		return out, nil
	}
	return nil, errors.New("INVALID PARAM")
}

func (c *server) RequestJoinVideoCall(ctx context.Context, in *pb.ShiPinRequestL6) (*pb.ShiPinRequestL6, error) {
	log.Println("call RequestJoinVideoCall", in)
	//Check if TO is OK
	if in.GetTO() == nil || in.GetTO().GetSPXX() == nil || in.GetTO().GetSPXX().GetID() == 0 {
		return nil, errors.New("INVALID PARAM")
	}
	//
	if in.GetJLSJ() == nil || in.GetJLSJ().GetSeconds() == 0 {
		in.JLSJ = &tm.Timestamp{Seconds: time.Now().Unix()}
	}
	if in.GetSXSJ() == nil || in.GetSXSJ().GetSeconds() == 0 {
		in.SXSJ = &tm.Timestamp{Seconds: time.Now().Unix() + VIDEO_REQUEST_TIMEOUT}
	}
	// if in.GetTO().GetLX() == pb.YHLX_KEFU {
	// 	in.ZL = pb.ShiPinRequestL6_QINGQIU
	// } else {
	// 	in.ZL = pb.ShiPinRequestL6_ZIDONG
	// }
	return c.SaveShiPinRequestL6(ctx, in)
}

func (c *server) RequestHangUpVideoCall(ctx context.Context, in *pb.ShiPinRequestL6) (*pb.ShiPinRequestL6, error) {
	log.Println("call RequestHangUpVideoCall", in)
	if in.GetJLSJ() == nil {
		in.JLSJ = &tm.Timestamp{Seconds: time.Now().Unix()}
	}
	if in.GetSXSJ() == nil {
		in.SXSJ = &tm.Timestamp{Seconds: time.Now().Unix() + VIDEO_REQUEST_TIMEOUT}
	}
	in.ZL = pb.ShiPinRequestL6_TUICHU
	return c.SaveShiPinRequestL6(ctx, in)
}

func (c *server) CancelVideoCallRequest(ctx context.Context, in *pb.ShiPinRequestL6) (*pb.ShiPinRequestL6, error) {
	log.Println("call CancelVideoCallRequest", in)
	in.SXSJ = &tm.Timestamp{Seconds: time.Now().Unix() - 1}
	s := new(shipin_request).fromPB(in)
	if err := s.SaveToDB(s.id); err != nil {
		return nil, err
	} else {
		return s.toPB(), nil
	}
}

// 客服，患者都可以设置所属医院，如果客服设置了所属医院，则只能抓取该医院所属的患者请求；
// 如果患者有所属医院，其请求会优先被该医院客服抓取，超时后才会被公司客服抓取，公司客服指没有设置所属医院的客服
// 如果患者没有所属医院，则请求会立刻被公司客服抓取
func (c *server) GetVideoCallRequestList(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ShiPinRequestListL7, error) {
	//log.Println("call GetVideoCallRequestList", in)
	//add yonghu to active list
	c.ReportVideoCallStatus(ctx, in)
	//get request by YongHu
	s := new(shipin_request)
	srs, err := s.SelectByPbShiPinYongHu(in)
	// log.Println("GetVideoCallRequestList-1")
	if err != nil {
		return nil, err
	}
	// log.Println("GetVideoCallRequestList-2")
	//no guahao in request, no need to update
	if len(in.GetXGGHS()) == 0 {
		return srs, nil
	}
	//请求里的挂号，需要更新排队数据
	out := &pb.ShiPinRequestListL7{
		SPRS: make([]*pb.ShiPinRequestL6, 0),
	}
	if srs != nil {
		out.SPRS = srs.GetSPRS()
	}
	// log.Println("GetVideoCallRequestList-3")
	// Update latest GuaHao paidui info
	g := make([]*pb.GuaHao, 0)
	for _, v := range in.GetXGGHS() {
		// log.Println("Update GuaHao PaiDui Info:", v)
		g = append(g, new(guahao).fromPB(v).toPB())
	}
	in.XGGHS = g
	r0 := &pb.ShiPinRequestL6{
		FROM: in,
		ZL:   pb.ShiPinRequestL6_UPDATE,
	}
	// log.Println("ShiPinRequestL6 Update:", r0)
	out.SPRS = append(out.SPRS, r0)
	return out, nil
}

func (c *server) TakeVideoCallRequest(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ShiPinRequestL6, error) {
	log.Println("call TakeVideoCallRequest")
	if reqs, err := c.GetVideoCallRequestList(ctx, in); err != nil {
		return nil, err
	} else {
		if len(reqs.GetSPRS()) > 0 {
			out := reqs.GetSPRS()[0]
			return c.RemoveVideoCallRequest(ctx, out)
		} else {
			return nil, errors.New("NOT EXIST")
		}
	}
}

func (c *server) SaveShiPinRequestL6(ctx context.Context, in *pb.ShiPinRequestL6) (*pb.ShiPinRequestL6, error) {
	// log.Println("call SaveShiPinRequestL6", in)
	s := &shipin_request{}
	if err := s.SavePbShiPinRequest(in); err != nil {
		return nil, err
	}
	in.ID = s.id
	return in, nil
}

func (c *server) GenerateUserSig(ctx context.Context, in *pb.UserID) (*pb.ReturnMessage, error) {
	// log.Println("call GenerateUserSig", in)
	sig, err := tencentyun.GenSig(sdkappid, key, in.GetID(), 604800)
	if err != nil {
		return nil, errors.New("Generate User Signature Failed")
	}
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: sig}, nil
}

func (c *server) GetGuaHaoList(ctx context.Context, in *pb.GuaHao) (*pb.GuaHaoList, error) {
	// log.Println("GetGuaHaoList:", in)
	out := new(pb.GuaHaoList)
	if ghs, err := new(guahao).fromPB(in).GetGuaHaoList(); err != nil {
		return nil, err
	} else {
		//Add PaiDui info, it is calculated when running
		for _, v := range ghs {
			vp := v.toPB()
			if in.GetISVALID() == false || (vp.GetISVALID() == true) {
				out.GHS = append(out.GHS, vp)
			}
		}
		return out, nil
	}
}

func (c *server) GetGuaHaoDuiZhangList(ctx context.Context, in *pb.GuaHao) (*pb.GuaHaoList, error) {
	log.Println("GetGuaHaoDuiZhangList:", in)
	out := new(pb.GuaHaoList)
	if ghs, err := new(guahao).fromPB(in).GetGuaHaoDuiZhangList(); err != nil {
		return nil, err
	} else {
		for _, v := range ghs {
			out.GHS = append(out.GHS, v.toPB())
		}
		return out, nil
	}
}

func (c *server) GetShiPinList(ctx context.Context, in *pb.ShiPinInfoL1) (*pb.ShiPinListL2, error) {
	log.Println("GetShiPinList", in)
	w := new(shipin).fromPB(in)
	if sps, err := w.GetShiPinList(); err != nil {
		return nil, err
	} else {
		out := &pb.ShiPinListL2{SPS: make([]*pb.ShiPinInfoL1, 0)}
		for _, v := range sps {
			out.SPS = append(out.SPS, v.toPB())
		}
		return out, nil
	}
}

func (c *server) GetMenZhenPaiBanList(ctx context.Context, in *pb.MenZhenPaiBan) (*pb.MenZhenPaiBanList, error) {
	log.Println("GetMenZhenPaiBanList", in)
	w := new(menzhen_paiban).fromPB(in)
	if pbs, err := w.GetMenZhenPaiBanList(); err != nil {
		log.Println(err)
		return nil, err
	} else {
		out := new(pb.MenZhenPaiBanList)
		for _, v := range pbs {
			out.MZS = append(out.MZS, v.toPB())
		}
		return out, nil
	}
}

func (s *server) SaveGuaHao(ctx context.Context, in *pb.GuaHao) (*pb.ReturnMessage, error) {
	log.Println("SaveGuaHao", in)
	w := new(guahao).fromPB(in)
	if len(w.orderid) > 0 {
		if err := w.SaveToDBByOrderid(); err != nil {
			if err := w.SaveToDB(w.id); err != nil {
				log.Println(err)
				return nil, err
			}
		}
	} else {
		if err := w.SaveToDB(w.id); err != nil {
			log.Println(err)
			return nil, err
		}
	}
	//add GuaHao to MenZhen PaiDui if not in
	menZhenPaiDui.AddGuaHaoToPaiDui(w.toPB())
	out := &pb.ReturnMessage{
		Code:    pb.ReturnMessage_OK,
		Message: fmt.Sprint(w.id),
	}
	return out, nil
}

func (s *server) GetHouZhenPaiDuiList(ctx context.Context, in *pb.HouZhenPaiDui) (*pb.HouZhenPaiDuiList, error) {
	pds, err := new(houzhen_paidui).fromPB(in).GetPaiDuiList()
	if err != nil {
		log.Println(err)
		return nil, err
	}
	out := new(pb.HouZhenPaiDuiList)
	for _, v := range pds {
		var t = v.toPB()
		if t != nil {
			out.PDS = append(out.PDS, t)
		}
	}
	return out, nil
}

func (c *server) GetGuaHaoHouZhenPaiDui(ctx context.Context, in *pb.GuaHao) (*pb.HouZhenPaiDui, error) {
	p := &houzhen_paidui{
		riqi:   GetNowHx(0),
		guahao: in.GetID(),
	}
	if err := p.GetByGuaHao(); err != nil {
		log.Println(err)
		return nil, err
	}
	return p.toPB(), nil
}

func (c *server) MenZhenCurrent(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.GuaHao, error) {
	log.Println("MenZhenCurrent", in)
	// return GetJiaoHao(in)
	return nil, nil
}

func (c *server) MenZhenNext(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.GuaHao, error) {
	log.Println("MenZhenNext", in)
	// return menZhenPaiDui.GetNextPaiDuiGuaHao(in)
	return nil, nil
}

func (c *server) MenZhenJiaoHao(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ReturnMessage, error) {
	log.Println("MenZhenJiaoHao:", in)
	// if len(in.GetXGGHS()) == 0 {
	// 	return nil, errors.New("INVALID PARAM")
	// }
	// if _, err := menZhenPaiDui.JiaoHao(in); err != nil {
	// 	return nil, err
	// } else {
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: "OK"}, nil
	// }
}

func (c *server) MenZhenInform(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ShiPinRequestListL7, error) {
	log.Println("MenZhenInform:", in)
	// if len(in.GetXGGHS()) == 0 {
	// 	log.Println("TO has no GuaHao info")
	// 	return nil, errors.New("INVALID PARAM")
	// }
	// if reqs := menZhenPaiDui.InformPaiDuiUser(in); reqs != nil {
	// 	for _, r := range reqs.GetSPRS() {
	// 		// log.Println("REQ:", r)
	// 		c.RequestJoinVideoCall(ctx, r)
	// 		return reqs, nil
	// 	}
	// }
	return nil, errors.New("NOT FOUND")
}

func (c *server) MenZhenGuoHao(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ReturnMessage, error) {
	log.Println("MenZhenGuoHao:", in)
	// if len(in.GetXGGHS()) == 0 {
	// 	return nil, errors.New("INVALID PARAM")
	// }
	// g := in.GetXGGHS()[0]
	// g.ZT = pb.GuaHao_HOUZHEN_SKIPPED
	// if gh := menZhenPaiDui.SetGuaHaoPaiDuiZT(g); gh != nil {
	// 	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: "OK"}, nil
	// } else {
	return nil, errors.New("Cannot found")
	// }
}

func (c *server) MenZhenQuery(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ReturnMessage, error) {
	log.Println("MenZhenQuery:", in)
	// if len(in.GetXGGHS()) == 0 {
	// 	return nil, errors.New("INVALID PARAM")
	// }
	// g := in.GetXGGHS()[0]
	// g.ZT = pb.GuaHao_HOUZHEN_QUERYING
	// if gh := menZhenPaiDui.SetGuaHaoPaiDuiZT(g); gh != nil {
	// 	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: "OK"}, nil
	// } else {
	return nil, errors.New("Cannot found")
	// }
}

func (c *server) MenZhenWanCheng(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ReturnMessage, error) {
	log.Println("MenZhenWanCheng:", in)
	// gh := in.GetXGGHS()[0]
	// gh.ZT = pb.GuaHao_HOUZHEN_FINISHED
	// gh.JZRQ = &tm.Timestamp{Seconds: time.Now().Unix()}
	// if g := menZhenPaiDui.SetGuaHaoPaiDuiZT(gh); g != nil {
	return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: "OK"}, nil
	// } else {
	// 	return nil, errors.New("Cannot found")
	// }
}

func (c *server) PaiDuiJiaoHao(ctx context.Context, in *pb.HouZhenPaiDui) (*pb.HouZhenPaiDui, error) {
	log.Println("PaiDuiJiaoHao1:", in)
	p := new(houzhen_paidui).fromPB(in)
	if p.doctor == 0 {
		return nil, errors.New("Invalid Doctor")
	}
	log.Println("PaiDuiJiaoHao2:")
	if err := p.GetFromDB(p.id); err != nil {
		log.Println("err:", err)
		return nil, err
	} else {
		log.Println("PaiDuiJiaoHao3:")
		if p.doctor != 0 && p.doctor != in.GetDOCTOR().GetID() {
			log.Println("PaiDuiJiaoHao4:")
			return nil, errors.New("JiaoHao ShiBai")
		} else {
			log.Println("PaiDuiJiaoHao5:")
			p.doctor = in.GetDOCTOR().GetID()
			log.Println("doctor:", p)
			if err := p.UpdateDoctor(); err != nil {
				log.Println("err:", err)
				return nil, err
			} else {
				log.Println("PaiDuiJiaoHao success")
				return p.toPB(), nil
			}
		}
	}
}

func (c *server) PaiDuiSetStatus(ctx context.Context, in *pb.HouZhenPaiDui) (*pb.HouZhenPaiDui, error) {
	log.Println("PaiDuiSetStatus:", in)
	p := new(houzhen_paidui).fromPB(in)
	if p.doctor == 0 {
		return nil, errors.New("Invalid Doctor")
	}
	if err := p.GetFromDB(p.id); err != nil {
		return nil, err
	} else {
		if p.doctor != 0 && p.doctor != in.GetDOCTOR().GetID() {
			return nil, errors.New("Set PaiDui Status ShiBai")
		} else {
			p.status = in.GetSTATUS()
			p.UpdateStatus()
			//check if need to set GUAHAO status
			if p.status == 4 { //结束
				g := &guahao{id: p.guahao}
				if err := g.GetFromDB(g.id); err != nil {
					log.Println("Complete GuaHao Error:", p.guahao, err)
				} else {
					g.status, g.statusname = 0, "结束"
					if g.jzrq == 0 {
						g.jzrq = time.Now().Unix()
						g.SaveToDB(g.id)
					} else {
						g.UpdateStatus()
					}
				}
			}
			return p.toPB(), nil
		}
	}
}

func (c *server) DengLuShuJuVerify(ctx context.Context, in *pb.DengLuShuJuL1) (*pb.ReturnMessage, error) {
	//log.Println("DengLuShuJuVerify:", in)
	d := new(denglu_shuju).fromPB(in)
	if err := d.Verify(); err != nil {
		return nil, errors.New("FAIL")
	} else {
		return &pb.ReturnMessage{Code: pb.ReturnMessage_OK, Message: "OK"}, nil
	}
}

func (c *server) GetVideoCallNeedResume(ctx context.Context, in *pb.ShiPinYongHuL5) (*pb.ShiPinYongHuL5, error) {
	log.Println("GetVideoCallNeedResume:", in)
	y := new(shipin_yonghu).fromPB(in)
	if err := y.GetFromDBByYh(); err != nil {
		return nil, errors.New("Not Found")
	}
	// 上次通话时间不超过一天，过0点
	t := time.Unix(y.jrsj, 0)
	if t.Day() != time.Now().Day() {
		return nil, errors.New("Over time")
	}
	// 有有效挂号
	if len(y.xggh) == 0 {
		return nil, errors.New("No guahao")
	}
	out := y.toPB()
	// 候诊/门诊视频房间没有关闭
	if out.GetSPXX().GetGBSJ().GetSeconds() > 0 {
		return nil, errors.New("Shipin closed")
	}
	for _, g := range out.GetXGGHS() {
		// 有有效挂号
		if g.GetISVALID() == true {
			return out, nil
		}
	}
	return nil, errors.New("No Valid Guahao")
}

func (s *server) SaveGuaHaoShuJu(ctx context.Context, in *pb.GuaHaoShuJu) (*pb.GuaHaoShuJu, error) {
	log.Println("SaveGuaHaoShuJu:", in)
	sj := new(guahao_shuju).fromPB(in)
	if sj.create_time == 0 {
		sj.create_time = time.Now().Unix()
	}
	sj.icd_list = make([]int64, 0)
	for _, v := range in.GetICDS() {
		icd := new(icd).fromPB(v)
		if err := icd.SaveToDB(icd.id); err != nil {
			log.Println(err)
		} else {
			sj.icd_list = append(sj.icd_list, int64(icd.id))
		}
	}
	if err := sj.SaveToDB(sj.id); err != nil {
		return nil, err
	} else {
		return sj.toPB(), nil
	}
}

func (s *server) GetGuaHaoShuJu(ctx context.Context, in *pb.GuaHaoShuJu) (*pb.GuaHaoShuJu, error) {
	log.Println("GetGuaHaoShuJu:", in)
	sj := new(guahao_shuju).fromPB(in)
	if err := sj.GetFromDBByGuaHao(); err != nil {
		log.Println(err)
		return nil, err
	} else {
		return sj.toPB(), nil
	}
}

func (c *server) GetHospital(ctx context.Context, in *pb.YiYuan) (*pb.YiYuan, error) {
	yy := new(yiyuan).fromPB(in)
	if yy.id > 0 {
		if err := yy.GetFromDB(yy.id); err != nil {
			return nil, err
		}
		return yy.toPB(), nil
	}
	if len(yy.mc) > 0 {
		if err := yy.GetFromDBByMC(yy.mc); err != nil {
			return nil, err
		}
		return yy.toPB(), nil
	}
	if len(yy.bm) > 0 {
		if err := yy.GetFromDBByBM(yy.bm); err != nil {
			return nil, err
		}
		return yy.toPB(), nil
	}
	return nil, errors.New("Invalid Param")
}

func (c *server) GetHospitalWithinDistance(ctx context.Context, in *pb.AreaPointRange) (*pb.YiYuanList, error) {
	yy := new(yiyuan)
	if yys, err := yy.GetYiYuanListWithinDistance(in.GetLat(), in.GetLng(), in.GetDistance()); err != nil {
		return nil, err
	} else {
		out := &pb.YiYuanList{YYS: make([]*pb.YiYuan, 0)}
		for _, v := range yys {
			out.YYS = append(out.YYS, v.toPB())
		}
		return out, nil
	}
}

func (c *server) GetHospitalByArea(ctx context.Context, in *pb.Area) (*pb.YiYuanList, error) {
	yy := &yiyuan{
		area:  in.GetArea(),
		area2: in.GetArea2(),
		area3: in.GetArea3(),
	}
	if yys, err := yy.GetYiYuanList(); err != nil {
		return nil, err
	} else {
		out := &pb.YiYuanList{YYS: make([]*pb.YiYuan, 0)}
		for _, v := range yys {
			out.YYS = append(out.YYS, v.toPB())
		}
		return out, nil
	}
}

func GetNowHx(h int) int64 {
	t := time.Now()
	y, m, d := t.Date()
	return time.Date(y, m, d, h, 0, 0, 0, time.Local).Unix()
}

func (c *server) GetUserStat(ctx context.Context, in *pb.UserStat) (*pb.UserStatList, error) {
	out := new(pb.UserStatList)
	var us []*UserContent
	if in.GetStat() == pb.UserStat_ALL {
		us = GetOnlineUser()
	} else if in.GetStat() == pb.UserStat_IN_VIDEO && len(in.GetRoom()) > 0 {
		us = GetOnlineUserByShiPin(in.GetRoom())
	}

	for _, v := range us {
		u := &pb.UserStat{
			Pid:        v.pid,
			Tp:         v.tp,
			Stat:       (pb.UserStat_STAT)(v.stat),
			ActiveTime: v.active_time,
			EnterTime:  v.enter_time,
			OnlineTime: v.online_time,
			Room:       v.room,
			Device:     v.device,
		}
		if v.guahao != nil {
			u.Guahao = v.guahao.toPB()
		}
		if v.user != nil {
			u.User = v.user.toPB()
		}
		out.USS = append(out.USS, u)
	}
	return out, nil
}

func (c *server) GetVip(ctx context.Context, in *pb.Vip) (*pb.Vip, error) {
	log.Println("GetVip:", in)

	vip := new(vip)
	vip.fromPB(in)

	if vip.id > 0 {
		err := vip.GetFromDB(vip.id)
		if err != nil {
			return nil, err
		}
		return vip.toPB(), nil
	}

	if len(strings.TrimSpace(vip.shenfenzheng)) > 0 {
		err := vip.GetFromDBBySFZ(strings.TrimSpace(vip.shenfenzheng))
		if err != nil {
			return nil, err
		}
		return vip.toPB(), nil
	}

	if len(strings.TrimSpace(vip.name)) > 0 {
		err := vip.GetFromDBByName(strings.TrimSpace(vip.name))
		if err != nil {
			return nil, err
		}
		return vip.toPB(), nil
	}

	return nil, errors.New("Invalid Param")
}

func (c *server) SetVip(ctx context.Context, in *pb.Vip) (*pb.Vip, error) {
	log.Println("SetVip:", in)

	vip := new(vip)
	vip.fromPB(in)

	if vip.id > 0 {
		err := vip.SaveToDB(vip.id)
		if err != nil {
			return nil, err
		}
		return vip.toPB(), nil
	} else {
		err := vip.SaveToDB(0)
		if err != nil {
			return nil, err
		}
		return vip.toPB(), nil
	}
}

//"加班安排",
//"规章制度",
//"联系我们",
//"信息公告",

func (c *server) GetTitle(ctx context.Context, in *pb.MenuTitle) (*pb.MenuTitleList, error) {
	log.Println("GetTitle:", in)

	titles := []*pb.MenuTitle{
		{
			ID:    0,
			Title: "最佳员工",
			// Title: "专家介绍",
		},
		{
			ID:    1,
			Title: "规章制度",
			// Title: "最新排班",
		},
		{
			ID:    2,
			Title: "工期安排",
			// Title: "健康常识",
		},
		{
			ID:    3,
			Title: "联系我们",
		},
		{
			ID:    4,
			Title: "信息公告",
		},
	}

	ts := new(pb.MenuTitleList)
	ts.Titles = make([]*pb.MenuTitle, 0)
	if in.ID > 0 {
		ts.Titles = append(ts.Titles, titles[in.ID])
	} else {
		ts.Titles = append(ts.Titles, titles...)
	}

	return ts, nil
}

func (c *server) GetTitleSH(ctx context.Context, in *pb.MenuTitle) (*pb.MenuTitleList, error) {
	log.Println("GetTitleSH:", in)

	titles := []*pb.MenuTitle{
		{
			// 广告1
			ID:      0,
			Title:   "广告1",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b1logo.jpeg",
			LinkURL: "",
		},
		{
			// 广告2
			ID:      1,
			Title:   "广告2",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b2logo.jpeg",
			LinkURL: "",
		},
		{
			// 广告3
			ID:      2,
			Title:   "广告3",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b3logo.jpeg",
			LinkURL: "",
		},
		{
			// 护理到家
			ID:      3,
			Title:   "报告查询",
			LinkURL: "https://service.bhjj120.cn/upload/xcx/hldj.html",
		},
		{
			ID:      4,
			Title:   "最佳员工",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b5logo.png",
			LinkURL: "https://service.bhjj120.cn/upload/xcx/zjjs1.html",
		},
		{
			ID:      5,
			Title:   "规章制度",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b6logo.png",
			LinkURL: "https://service.bhjj120.cn/upload/xcx/pbap1.html",
		},
		{
			ID:      6,
			Title:   "联系方式",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b8logo.png",
			LinkURL: "",
		},
	}

	ts := new(pb.MenuTitleList)
	ts.Titles = make([]*pb.MenuTitle, 0)
	if in.ID > 0 {
		ts.Titles = append(ts.Titles, titles[in.ID])
	} else {
		ts.Titles = append(ts.Titles, titles...)
	}

	return ts, nil
}

func (c *server) GetTitleRun(ctx context.Context, in *pb.MenuTitle) (*pb.MenuTitleList, error) {
	log.Println("GetTitleRun:", in)

	titles := []*pb.MenuTitle{
		{
			// 广告1
			ID:      0,
			Title:   "广告1",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b1logo.jpeg",
			LinkURL: "",
		},
		{
			// 广告2
			ID:      1,
			Title:   "广告2",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b2logo.jpeg",
			LinkURL: "",
		},
		{
			// 广告3
			ID:      2,
			Title:   "广告3",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b3logo.jpeg",
			LinkURL: "",
		},
		{
			// 护理到家
			ID: 3,
			// Title: "报告查询",
			Title:   "联馨护理",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b4logo.png",
			// LinkURL: "https://service.bhjj120.cn/upload/xcx/hldj.html",
			LinkURL: "https://service.bhjj120.cn/upload/xcx/nurse/nurse.html",
		},
		{
			ID: 4,
			// Title: "最佳员工",
			Title:   "远程专家",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b5logo.png",
			// LinkURL: "https://service.bhjj120.cn/upload/xcx/zjjs1.html",
			LinkURL: "https://service.bhjj120.cn/upload/xcx/zjjs2.html",
		},
		// {
		// 	ID: 5,
		// 	// Title: "规章制度",
		// 	Title:   "远程排班",
		// 	LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b6logo.png",
		// 	// LinkURL: "https://service.bhjj120.cn/upload/xcx/pbap1.html",
		// 	LinkURL: "https://service.bhjj120.cn/upload/xcx/pbap3.html",
		// },
		{
			ID: 5,
			// Title: "规章制度",
			Title:   "本院专家",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b5logo.png",
			// LinkURL: "https://service.bhjj120.cn/upload/xcx/pbap1.html",
			LinkURL: "https://service.bhjj120.cn/upload/xcx/zjjs4.html",
		},
		{
			ID:      6,
			Title:   "联系方式",
			LogoURL: "https://service.bhjj120.cn/upload/xcx/logo/b8logo.png",
			LinkURL: "",
		},
	}

	ts := new(pb.MenuTitleList)
	ts.Titles = make([]*pb.MenuTitle, 0)
	if in.ID > 0 {
		ts.Titles = append(ts.Titles, titles[in.ID])
	} else {
		ts.Titles = append(ts.Titles, titles...)
	}

	return ts, nil
}

func (c *server) GetTitleV2(ctx context.Context, in *pb.MenuTitle) (*pb.MenuTitleList, error) {
	log.Println("GetTitleV2:", in)

	return c.GetTitleRun(ctx, in)
}

func (c *server) VideoPadSettingsGet(ctx context.Context, in *pb.VideoPadSettings) (*pb.VideoPadSettings, error) {
	log.Println("VideoPadSettingsGet:", in)
	if in.GetHZID() == 0 {
		return nil, errors.New("no hzid")
	}
	settings := &VideoPadSettings{}
	err := settings.GetFromDBByHZID(in.GetHZID())
	if err != nil {
		return nil, err
	}
	return settings.toPB(), nil
}

func (c *server) VideoPadSettingsSave(ctx context.Context, in *pb.VideoPadSettings) (*pb.VideoPadSettings, error) {
	log.Println("VideoPadSettingsSave:", in)
	if in.GetHZID() == 0 {
		return nil, errors.New("no hzid")
	}
	old := &VideoPadSettings{}
	old.GetFromDBByHZID(in.GetHZID())
	log.Println("old:", old)
	settings := &VideoPadSettings{}
	settings.fromPB(in)
	settings.id = old.id
	log.Println("settings:", settings)
	err := settings.SaveToDB(settings.id)
	if err != nil {
		return nil, err
	}
	return settings.toPB(), nil
}

func (c *server) VideoPadPhotoGet(ctx context.Context, in *pb.VideoPadSettings) (*pb.VideoPadPhotoList, error) {
	log.Println("call VideoPadPhotoGet", in)
	if in.GetHZID() > 0 {
		h := &VideoPadPhoto{
			hzid: in.HZID,
		}
		pbs, err := h.Search()
		if err != nil {
			return nil, err
		}
		ret := &pb.VideoPadPhotoList{
			Photos: make([]*pb.VideoPadPhoto, 0),
		}
		for _, v := range pbs {
			ret.Photos = append(ret.Photos, v.toPB())
		}
		return ret, nil
	}
	return nil, errors.New("NOT EXIST")
}
